home *** CD-ROM | disk | FTP | other *** search
/ BCI NET 2 / BCI NET 2.iso / archives / utilities / text / less-278.lha / less-278 / src.lha / source / edit.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-01  |  12.7 KB  |  656 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice in the documentation and/or other materials provided with 
  12.  *    the distribution.
  13.  *
  14.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
  15.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  17.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
  18.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  19.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
  20.  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
  21.  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  22.  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
  23.  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
  24.  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25.  */
  26.  
  27.  
  28. #include "less.h"
  29.  
  30. public int fd0 = 0;
  31.  
  32. extern int new_file;
  33. extern int errmsgs;
  34. extern int quit_at_eof;
  35. extern int cbufs;
  36. extern char *every_first_cmd;
  37. extern int any_display;
  38. extern int force_open;
  39. extern int is_tty;
  40. extern IFILE curr_ifile;
  41. extern IFILE old_ifile;
  42. extern struct scrpos initial_scrpos;
  43.  
  44. #if LOGFILE
  45. extern int logfile;
  46. extern int force_logfile;
  47. extern char *namelogfile;
  48. #endif
  49.  
  50. static char *curr_altfilename = NULL;
  51. static void *curr_altpipe;
  52.  
  53.  
  54. /*
  55.  * Textlist functions deal with a list of words separated by spaces.
  56.  * init_textlist sets up a textlist structure.
  57.  * forw_textlist uses that structure to iterate thru the list of
  58.  * words, returning each one as a standard null-terminated string.
  59.  * back_textlist does the same, but runs thru the list backwards.
  60.  */
  61.     public void
  62. init_textlist(tlist, str)
  63.     struct textlist *tlist;
  64.     char *str;
  65. {
  66.     char *s;
  67.     
  68.     tlist->string = skipsp(str);
  69.     tlist->endstring = tlist->string + strlen(tlist->string);
  70.     for (s = str;  s < tlist->endstring;  s++)
  71.     {
  72.         if (*s == ' ')
  73.             *s = '\0';
  74.     }
  75. }
  76.  
  77.     public char *
  78. forw_textlist(tlist, prev)
  79.     struct textlist *tlist;
  80.     char *prev;
  81. {
  82.     char *s;
  83.     
  84.     /*
  85.      * prev == NULL means return the first word in the list.
  86.      * Otherwise, return the word after "prev".
  87.      */
  88.     if (prev == NULL)
  89.         s = tlist->string;
  90.     else
  91.         s = prev + strlen(prev);
  92.     if (s >= tlist->endstring)
  93.         return (NULL);
  94.     while (*s == '\0')
  95.         s++;
  96.     if (s >= tlist->endstring)
  97.         return (NULL);
  98.     return (s);
  99. }
  100.  
  101.     public char *
  102. back_textlist(tlist, prev)
  103.     struct textlist *tlist;
  104.     char *prev;
  105. {
  106.     char *s;
  107.     
  108.     /*
  109.      * prev == NULL means return the last word in the list.
  110.      * Otherwise, return the word before "prev".
  111.      */
  112.     if (prev == NULL)
  113.         s = tlist->endstring;
  114.     else if (prev <= tlist->string)
  115.         return (NULL);
  116.     else
  117.         s = prev - 1;
  118.     while (*s == '\0')
  119.         s--;
  120.     if (s <= tlist->string)
  121.         return (NULL);
  122.     while (s[-1] != '\0' && s > tlist->string)
  123.         s--;
  124.     return (s);
  125. }
  126.  
  127. /*
  128.  * Close the current input file.
  129.  */
  130.     static void
  131. close_file()
  132. {
  133.     struct scrpos scrpos;
  134.     
  135.     if (curr_ifile == NULL_IFILE)
  136.         return;
  137.     /*
  138.      * Save the current position so that we can return to
  139.      * the same position if we edit this file again.
  140.      */
  141.     get_scrpos(&scrpos);
  142.     if (scrpos.pos != NULL_POSITION)
  143.     {
  144.         store_pos(curr_ifile, &scrpos);
  145.         lastmark();
  146.     }
  147.     /*
  148.      * Close the file descriptor, unless it is a pipe.
  149.      */
  150.     ch_close();
  151.     /*
  152.      * If we opened a file using an alternate name,
  153.      * do special stuff to close it.
  154.      */
  155.     if (curr_altfilename != NULL)
  156.     {
  157.         close_altfile(curr_altfilename, get_filename(curr_ifile),
  158.             curr_altpipe);
  159.         free(curr_altfilename);
  160.         curr_altfilename = NULL;
  161.     }
  162.     curr_ifile = NULL_IFILE;
  163. }
  164.  
  165. /*
  166.  * Edit a new file (given its name).
  167.  * Filename == "-" means standard input.
  168.  * Filename == NULL means just close the current file.
  169.  */
  170.     public int
  171. edit(filename)
  172.     char *filename;
  173. {
  174.     if (filename == NULL)
  175.         return (edit_ifile(NULL_IFILE));
  176.     return (edit_ifile(get_ifile(filename, curr_ifile)));
  177. }
  178.     
  179. /*
  180.  * Edit a new file (given its IFILE).
  181.  * ifile == NULL means just close the current file.
  182.  */
  183.     public int
  184. edit_ifile(ifile)
  185.     IFILE ifile;
  186. {
  187.     int f;
  188.     int answer;
  189.     int no_display;
  190.     int chflags;
  191.     char *filename;
  192.     char *open_filename;
  193.     char *alt_filename;
  194.     void *alt_pipe;
  195.     PARG parg;
  196.         
  197.     if (ifile == curr_ifile)
  198.     {
  199.         /*
  200.          * Already have the correct file open.
  201.          */
  202.         return (0);
  203.     }
  204.  
  205.     /*
  206.      * We must close the currently open file now.
  207.      * This is necessary to make the open_altfile/close_altfile pairs
  208.      * nest properly (or rather to avoid nesting at all).
  209.      * {{ Some stupid implementations of popen() mess up if you do:
  210.      *    fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
  211.      */
  212. #if LOGFILE
  213.     end_logfile();
  214. #endif
  215.     if (curr_ifile != NULL_IFILE)
  216.     {
  217.         old_ifile = curr_ifile;
  218.         close_file();
  219.     }
  220.  
  221.     if (ifile == NULL_IFILE)
  222.         /*
  223.          * No new file to open.
  224.          */
  225.         return (0);
  226.  
  227.     filename = get_filename(ifile);
  228.     /*
  229.      * See if LESSOPEN specifies an "alternate" file to open.
  230.      */
  231.     alt_pipe = NULL;
  232.     alt_filename = open_altfile(filename, &f, &alt_pipe);
  233.     open_filename = (alt_filename != NULL) ? alt_filename : filename;
  234.  
  235.     chflags = 0;
  236.     if (alt_pipe != NULL)
  237.     {
  238.         /*
  239.          * The alternate "file" is actually a pipe.
  240.          * f has already been set to the file descriptor of the pipe
  241.          * in the call to open_altfile above.
  242.          * Keep the file descriptor open because it was opened 
  243.          * via popen(), and pclose() wants to close it.
  244.          */
  245.         chflags |= CH_POPENED;
  246.     } else if (strcmp(open_filename, "-") == 0)
  247.     {
  248.         /* 
  249.          * Use standard input.
  250.          * Keep the file descriptor open because we can't reopen it.
  251.          */
  252.         f = fd0;
  253.         chflags |= CH_KEEPOPEN;
  254.     } else if ((parg.p_string = bad_file(open_filename)) != NULL)
  255.     {
  256.         /*
  257.          * It looks like a bad file.  Don't try to open it.
  258.          */
  259.         error("%s", &parg);
  260.         free(parg.p_string);
  261.         err1:
  262.         if (alt_filename != NULL)
  263.         {
  264.             close_altfile(alt_filename, filename, alt_pipe);
  265.             free(alt_filename);
  266.         }
  267.         del_ifile(ifile);
  268.         /*
  269.          * Re-open the current file.
  270.          */
  271.         (void) edit_ifile(old_ifile);
  272.         return (1);
  273.     } else if ((f = open(open_filename, OPEN_READ)) < 0)
  274.     {
  275.         /*
  276.          * Got an error trying to open it.
  277.          */
  278.         parg.p_string = errno_message(filename);
  279.         error("%s", &parg);
  280.         free(parg.p_string);
  281.             goto err1;
  282.     } else if (!force_open && !opened(ifile) && bin_file(f))
  283.     {
  284.         /*
  285.          * Looks like a binary file.  Ask user if we should proceed.
  286.          */
  287.         parg.p_string = filename;
  288.         answer = query("\"%s\" may be a binary file.  See it anyway? ",
  289.             &parg);
  290.         if (answer != 'y' && answer != 'Y')
  291.         {
  292.             close(f);
  293.             goto err1;
  294.         }
  295.     }
  296.  
  297.     /*
  298.      * Get the new ifile.
  299.      * Get the saved position for the file.
  300.      */
  301.     curr_ifile = ifile;
  302.     curr_altfilename = alt_filename;
  303.     curr_altpipe = alt_pipe;
  304.     set_open(curr_ifile); /* File has been opened */
  305.     get_pos(curr_ifile, &initial_scrpos);
  306.     new_file = TRUE;
  307.     ch_init(f, chflags);
  308. #if LOGFILE
  309.     if (namelogfile != NULL && is_tty)
  310.         use_logfile(namelogfile);
  311. #endif
  312.  
  313.     if (every_first_cmd != NULL)
  314.         ungetsc(every_first_cmd);
  315.  
  316.     no_display = !any_display;
  317.     flush();
  318.     any_display = TRUE;
  319.  
  320.     if (is_tty)
  321.     {
  322.         /*
  323.          * Output is to a real tty.
  324.          */
  325.  
  326.         /*
  327.          * Indicate there is nothing displayed yet.
  328.          */
  329.         pos_clear();
  330.         clr_linenum();
  331. #if HILITE_SEARCH
  332.         clr_hilite();
  333. #endif
  334.         if (no_display && errmsgs > 0)
  335.         {
  336.             /*
  337.              * We displayed some messages on error output
  338.              * (file descriptor 2; see error() function).
  339.              * Before erasing the screen contents,
  340.              * display the file name and wait for a keystroke.
  341.              */
  342.             parg.p_string = filename;
  343.             error("%s", &parg);
  344.         }
  345.     }
  346.     return (0);
  347. }
  348.  
  349. /*
  350.  * Edit a space-separated list of files.
  351.  * For each filename in the list, enter it into the ifile list.
  352.  * Then edit the first one.
  353.  */
  354.     public int
  355. edit_list(filelist)
  356.     char *filelist;
  357. {
  358.     IFILE save_curr_ifile;
  359.     char *good_filename;
  360.     char *filename;
  361.     char *gfilelist;
  362.     char *gfilename;
  363.     struct textlist tl_files;
  364.     struct textlist tl_gfiles;
  365.  
  366.     save_curr_ifile = curr_ifile;
  367.     good_filename = NULL;
  368.     
  369.     /*
  370.      * Run thru each filename in the list.
  371.      * Try to glob the filename.  
  372.      * If it doesn't expand, just try to open the filename.
  373.      * If it does expand, try to open each name in that list.
  374.      */
  375.     init_textlist(&tl_files, filelist);
  376.     filename = NULL;
  377.     while ((filename = forw_textlist(&tl_files, filename)) != NULL)
  378.     {
  379.         gfilelist = glob(filename);
  380.         if (gfilelist == NULL)
  381.         {
  382.             if (edit(filename) == 0 && good_filename == NULL)
  383.                 good_filename = get_filename(curr_ifile);
  384.         } else
  385.         {
  386.             init_textlist(&tl_gfiles, gfilelist);
  387.             gfilename = NULL;
  388.             while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
  389.             {
  390.                 if (edit(gfilename) == 0 && good_filename == NULL)
  391.                     good_filename = get_filename(curr_ifile);
  392.             }
  393.             free(gfilelist);
  394.         }
  395.     }
  396.     /*
  397.      * Edit the first valid filename in the list.
  398.      */
  399.     if (good_filename == NULL)
  400.         return (1);
  401.     if (get_ifile(good_filename, curr_ifile) == curr_ifile)
  402.         /*
  403.          * Trying to edit the current file; don't reopen it.
  404.          */
  405.         return (0);
  406.     if (edit_ifile(save_curr_ifile))
  407.         quit(QUIT_ERROR);
  408.     return (edit(good_filename));
  409. }
  410.  
  411. /*
  412.  * Edit the first file in the command line (ifile) list.
  413.  */
  414.     public int
  415. edit_first()
  416. {
  417.     curr_ifile = NULL_IFILE;
  418.     return (edit_next(1));
  419. }
  420.  
  421. /*
  422.  * Edit the last file in the command line (ifile) list.
  423.  */
  424.     public int
  425. edit_last()
  426. {
  427.     curr_ifile = NULL_IFILE;
  428.     return (edit_prev(1));
  429. }
  430.  
  431.  
  432. /*
  433.  * Edit the next file in the command line (ifile) list.
  434.  */
  435.     public int
  436. edit_next(n)
  437.     int n;
  438. {
  439.     IFILE h;
  440.     IFILE next;
  441.  
  442.     h = curr_ifile;
  443.     /*
  444.      * Skip n filenames, then try to edit each filename.
  445.      */
  446.     for (;;)
  447.     {
  448.         next = next_ifile(h);
  449.         if (--n < 0)
  450.         {
  451.             if (edit_ifile(h) == 0)
  452.                 break;
  453.         }
  454.         if (next == NULL_IFILE)
  455.         {
  456.             /*
  457.              * Reached end of the ifile list.
  458.              */
  459.             return (1);
  460.         }
  461.         h = next;
  462.     } 
  463.     /*
  464.      * Found a file that we can edit.
  465.      */
  466.     return (0);
  467. }
  468.  
  469. /*
  470.  * Edit the previous file in the command line list.
  471.  */
  472.     public int
  473. edit_prev(n)
  474.     int n;
  475. {
  476.     IFILE h;
  477.     IFILE next;
  478.  
  479.     h = curr_ifile;
  480.     /*
  481.      * Skip n filenames, then try to edit each filename.
  482.      */
  483.     for (;;)
  484.     {
  485.         next = prev_ifile(h);
  486.         if (--n < 0)
  487.         {
  488.             if (edit_ifile(h) == 0)
  489.                 break;
  490.         }
  491.         if (next == NULL_IFILE)
  492.         {
  493.             /*
  494.              * Reached beginning of the ifile list.
  495.              */
  496.             return (1);
  497.         }
  498.         h = next;
  499.     } 
  500.     /*
  501.      * Found a file that we can edit.
  502.      */
  503.     return (0);
  504. }
  505.  
  506. /*
  507.  * Edit a specific file in the command line (ifile) list.
  508.  */
  509.     public int
  510. edit_index(n)
  511.     int n;
  512. {
  513.     IFILE h;
  514.  
  515.     h = NULL_IFILE;
  516.     do
  517.     {
  518.         if ((h = next_ifile(h)) == NULL_IFILE)
  519.         {
  520.             /*
  521.              * Reached end of the list without finding it.
  522.              */
  523.             return (1);
  524.         }
  525.     } while (get_index(h) != n);
  526.  
  527.     return (edit_ifile(h));
  528. }
  529.  
  530. /*
  531.  * Edit standard input.
  532.  */
  533.     public int
  534. edit_stdin()
  535. {
  536.     if (isatty(fd0))
  537.     {
  538. #if MSOFTC || OS2
  539.         error("Missing filename (\"less -?\" for help)", NULL_PARG);
  540. #else
  541.         error("Missing filename (\"less -\\?\" for help)", NULL_PARG);
  542. #endif
  543.         quit(QUIT_OK);
  544.     }
  545.     return (edit("-"));
  546. }
  547.  
  548. /*
  549.  * Copy a file directly to standard output.
  550.  * Used if standard output is not a tty.
  551.  */
  552.     public void
  553. cat_file()
  554. {
  555.     register int c;
  556.  
  557.     while ((c = ch_forw_get()) != EOI)
  558.         putchr(c);
  559.     flush();
  560. }
  561.  
  562. #if LOGFILE
  563.  
  564. /*
  565.  * If the user asked for a log file and our input file
  566.  * is standard input, create the log file.  
  567.  * We take care not to blindly overwrite an existing file.
  568.  */
  569.     public void
  570. use_logfile(filename)
  571.     char *filename;
  572. {
  573.     register int exists;
  574.     register int answer;
  575.     PARG parg;
  576.  
  577.     if (ch_getflags() & CH_CANSEEK)
  578.         /*
  579.          * Can't currently use a log file on a file that can seek.
  580.          */
  581.         return;
  582.  
  583.     /*
  584.      * {{ We could use access() here. }}
  585.      */
  586.     exists = open(filename, OPEN_READ);
  587.     close(exists);
  588.     exists = (exists >= 0);
  589.  
  590.     /*
  591.      * Decide whether to overwrite the log file or append to it.
  592.      * If it doesn't exist we "overwrite" it.
  593.      */
  594.     if (!exists || force_logfile)
  595.     {
  596.         /*
  597.          * Overwrite (or create) the log file.
  598.          */
  599.         answer = 'O';
  600.     } else
  601.     {
  602.         /*
  603.          * Ask user what to do.
  604.          */
  605.         parg.p_string = filename;
  606.         answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg);
  607.     }
  608.  
  609. loop:
  610.     switch (answer)
  611.     {
  612.     case 'O': case 'o':
  613.         /*
  614.          * Overwrite: create the file.
  615.          */
  616.         logfile = creat(filename, 0644);
  617.         break;
  618.     case 'A': case 'a':
  619.         /*
  620.          * Append: open the file and seek to the end.
  621.          */
  622.         logfile = open(filename, OPEN_APPEND);
  623.         if (lseek(logfile, (off_t)0, 2) == BAD_LSEEK)
  624.         {
  625.             close(logfile);
  626.             logfile = -1;
  627.         }
  628.         break;
  629.     case 'D': case 'd':
  630.         /*
  631.          * Don't do anything.
  632.          */
  633.         return;
  634.     case 'q':
  635.         quit(QUIT_OK);
  636.         /*NOTREACHED*/
  637.     default:
  638.         /*
  639.          * Eh?
  640.          */
  641.         answer = query("Overwrite, Append, or Don't log? (Type \"O\", \"A\", \"D\" or \"q\") ", NULL_PARG);
  642.         goto loop;
  643.     }
  644.  
  645.     if (logfile < 0)
  646.     {
  647.         /*
  648.          * Error in opening logfile.
  649.          */
  650.         parg.p_string = filename;
  651.         error("Cannot write to \"%s\"", &parg);
  652.     }
  653. }
  654.  
  655. #endif
  656.